#include "./InputThread.h"
#include <iostream>

#include "../Commands/AlterBehaviourFlags.h"
#include "./InputDispatcher.h"

using namespace Input;

/* Docelowy odstep pomiedzy probkami wejscia (w ms). */
#define SAMPLING_STEP 25

InputThread::InputThread() {}

InputThread::~InputThread() {}

void InputThread::windowResized(Ogre::RenderWindow* rw) {
    unsigned int width, height, depth;
    int left, top;
	rw->getMetrics(width, height, depth, left, top);
    const OIS::MouseState &ms = Input::InputThread::getInstance().getMouse()->getMouseState();
    ms.width = width;
    ms.height = height;
}

void InputThread::run() {

	while( 1 ) {

		boost::this_thread::interruption_point();

		//przechwytujemy stan wejscia:
		keyboard_->capture();
		mouse_->capture();

		boost::posix_time::time_duration td = boost::posix_time::milliseconds( SAMPLING_STEP );
		boost::this_thread::sleep( td );

	}

}

void InputThread::init( size_t window_handle ) {
	OIS::ParamList pl;
	std::ostringstream window_handle_stream;

	window_handle_stream << window_handle;

	pl.insert(std::make_pair(std::string("WINDOW"), window_handle_stream.str()));
#ifdef _WIN32
	pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
	pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
#else

#endif

	input_manager_ = OIS::InputManager::createInputSystem( pl );

    keyboard_ = static_cast<OIS::Keyboard*>( input_manager_->createInputObject( OIS::OISKeyboard, true ) );
    mouse_ = static_cast<OIS::Mouse*>( input_manager_->createInputObject( OIS::OISMouse, true ) );

    mouse_->setEventCallback( this );
    keyboard_->setEventCallback( this );
}

/* Callback nacisniecia klawisza: */
bool InputThread::keyPressed( const OIS::KeyEvent &arg ) {
	Commands::Command* cmd = 0;

	switch( arg.key ) {

	case OIS::KC_W: //do gory
		cmd = new Commands::AlterBehaviourFlags< Commands::ctBegin >(
			getPlayerId(),
			GameLogic::Objects::GameObject::FLAG_UP
		);
		break;

	case OIS::KC_S: //do dolu
		cmd = new Commands::AlterBehaviourFlags< Commands::ctBegin >(
			getPlayerId(),
			GameLogic::Objects::GameObject::FLAG_DOWN
		);
		break;

	case OIS::KC_A: //strafe w lewo
		cmd = new Commands::AlterBehaviourFlags< Commands::ctBegin >(
			getPlayerId(),
			GameLogic::Objects::GameObject::FLAG_LEFT
		);
		break;

	case OIS::KC_D: //strafe w prawo
		cmd = new Commands::AlterBehaviourFlags< Commands::ctBegin >(
			getPlayerId(),
			GameLogic::Objects::GameObject::FLAG_RIGHT
		);
		break;

	default:
		break;
	}

	if( cmd != 0 ) {
		InputDispatcher::dispatchBehaviourMaskChange( cmd );
	}
	return true;
}

/* Callback zwolnienia klawisza: */
bool InputThread::keyReleased( const OIS::KeyEvent &arg ) {
	Commands::Command* cmd = 0;

	switch( arg.key ) {

	case OIS::KC_W: //do gory
		cmd = new Commands::AlterBehaviourFlags< Commands::ctEnd >(
			getPlayerId(),
			GameLogic::Objects::GameObject::FLAG_UP
		);
		break;

	case OIS::KC_S: //do dolu
		cmd = new Commands::AlterBehaviourFlags< Commands::ctEnd >(
			getPlayerId(),
			GameLogic::Objects::GameObject::FLAG_DOWN
		);
		break;

	case OIS::KC_A: //strafe w lewo
		cmd = new Commands::AlterBehaviourFlags< Commands::ctEnd >(
			getPlayerId(),
			GameLogic::Objects::GameObject::FLAG_LEFT
		);
		break;

	case OIS::KC_D: //strafe w prawo
		cmd = new Commands::AlterBehaviourFlags< Commands::ctEnd >(
			getPlayerId(),
			GameLogic::Objects::GameObject::FLAG_RIGHT
		);
		break;
	default:
		break;
	}

	if( cmd != 0 ) {
		InputDispatcher::dispatchBehaviourMaskChange( cmd );
	}
	return true;
}

/* Callback dla nacisniecia klawisza myszy. */
bool InputThread::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) {

	return true;
}

/* Callback dla puszczenia klawisza myszy. */
bool InputThread::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) {

	return true;
}

/* Callback dla zmiany polozenia ursora. */
bool InputThread::mouseMoved( const OIS::MouseEvent &arg ) {
	boost::mutex::scoped_lock lock( position_mutex_ );
	mouse_position_.x = arg.state.X.abs;
	mouse_position_.y = arg.state.Y.abs;
	return true;
}

const Vec2i InputThread::getMousePosition() {
	boost::mutex::scoped_lock lock( position_mutex_ );
	return mouse_position_;
	//TODO: sprawdzic, jak dziala powyzszy kod. Czy lock na pewno niszczony
	//jest po wykonaniu kopii mouse_position_?
}
